; * Load the kernel.
; * Switch into Protected Mode.
; * Initialize the kernel, in ELF format, and jump into the kernel.
; * Setup a simple page directory.

; Memory:
;
; begin:
;   +------+-------------+-----+------------+------------+----------+
;   |      | boot-sector |     | kernel.bin | loader.bin | Reserved |
;   +------+-------------+-----+------------+------------+----------+
;   0     7C00         7E00  80000h       90000h       9FC00h   1M(100000h)
;
; end:
;   +----+-----+----------+------------+------------+-------+------+------+----------+
;   |IDT | GDT | page-dir | page-tab-h | page-tab-l | kernel stack | info | Reserved |
;   +----+-----+----------+------------+------------+-------+------+------+----------+
;   0   2K    4K         8K           12K       16K(4000h)      9F000h  9FC00h  1M(100000h)




;==========

; Offset of kernel in virtual memory.
VIR_KERNEL		equ	0C0000000h	; 3G, like linux

; Information area, used by kernel.
INFO_BASE		equ	9F00h	; base
INFO_DISK		equ	0h	; disk information
INFO_MEMORY_NUM		equ	20h	; number of memory infor area
INFO_MEMORY		equ	24h	; memory information

; Address of IDT and GDT.
PHY_ADDR_IDT		equ	0
PHY_ADDR_GDT		equ	800h
VIR_ADDR_GDT		equ	(PHY_ADDR_GDT + VIR_KERNEL)

; Address of page-directory and page-tables.
PHY_ADDR_PAGE_DIR		equ	1000h
PHY_ADDR_PAGE_TABLE_HIGH	equ	2000h	; f(p) = p - 3G
PHY_ADDR_PAGE_TABLE_LOW		equ	3000h	; f(p) = p

; Top of kernel's stack.
TopStack		equ	9E000h + VIR_KERNEL

;==========

; The physical address of the loader. This's specified in boot/boot.asm.
PHY_ADDR_LOADER		equ	090000h

; Where the kernel is loaded for the moment.
BASE_KERNEL_FILE	equ	08000h
PHY_ADDR_KERNEL_FILE	equ	BASE_KERNEL_FILE * 010h

; Where the kernel lies on image. begin:length in sector.
IMG_BEGIN_KERNEL	equ	4
IMG_LEN_KERNEL		equ	127

;=== Beign loader! ===

; Let's begin the loader here!
HEAD_LABEL:
jmp	LABEL_BEGIN

; GDT - Global Descriptor Table
LABEL_GDT:	dw	0, 0, 0, 0
DESC_FLAT_C:	dw	0FFFFh,	00000h
		db	000h,	09Ah,	0CFh,	000h
DESC_FLAT_RW:	dw	0FFFFh, 00000h
		db	000h, 	092h,	0CFh,	000h
;DESC_FLAT_VIDEO:dw	0FFFFh, 08000h
;		db	00Bh,	0F2h,	000h,	000h
DESC_FLAT_VIDEO:dw	07FFFh, 08000h
		db	00Bh,	0F2h,	000h,	0C0h

gdt_ptr		dw	256 * 8 - 1
		dd	PHY_ADDR_GDT

gdt_ptr1	dw	256 * 8 - 1
		dd	VIR_ADDR_GDT

; Selectors.
sel_code	equ	DESC_FLAT_C	- LABEL_GDT
sel_data	equ	DESC_FLAT_RW	- LABEL_GDT
sel_video	equ	DESC_FLAT_VIDEO	- LABEL_GDT

LABEL_BEGIN:
	; Load the kernel.
	; es:BASE_KERNEL_FILE
	mov	ax, BASE_KERNEL_FILE
	mov	es, ax
	mov	bx, 0
	mov	ax, IMG_BEGIN_KERNEL
	mov	cl, 18
	div	cl
	mov	cl, ah
	inc	cl
	mov	ch, al
	shr	ch, 1
	mov	dh, al
	and	dh, 1
	mov	dl, 0	; 0 - floppya, 80h - disk
	mov	ah, 2
	mov	al, 32; IMG_LEN_KERNEL
	int	13h
	; Qemu, seems it can read 32 sectors once...
	mov	ax, BASE_KERNEL_FILE + 400h
	mov	es, ax
	mov	bx, 0
	mov	ax, IMG_BEGIN_KERNEL + 32
	mov	cl, 18
	div	cl
	mov	cl, ah
	inc	cl
	mov	ch, al
	shr	ch, 1
	mov	dh, al
	and	dh, 1
	mov	dl, 0	; 0 - floppya, 80h - disk
	mov	ah, 2
	mov	al, 32 ;IMG_LEN_KERNEL
	int	13h



	; Get 2 disks' information.
	; es:INFO_BASE
	mov	ax, INFO_BASE
	mov	es, ax
	mov	di, INFO_DISK
	lds	si, [4 * 41h]
	mov	ecx, 10h
	rep	movsb
	lds	si, [4 * 46h]
	mov	ecx, 10h
	rep	movsb

	; Get memory's information.
	; es:INFO_BASE
	xor	esi, esi	; number of memory erea
	xor	ebx, ebx
	mov	di, INFO_MEMORY
.mem:
	mov	eax, 0E820h
	mov	ecx, 20
	mov	edx, 0534D4150h
	int	15h
	jc	MEM_CHK_FAIL
	add	di, 20
	inc	esi
	cmp	ebx, 0
	jne	.mem
	mov	[es:INFO_MEMORY_NUM], esi
MEM_CHK_FAIL:
	

	; Set the GDT at 2K.
	; es:0	ds:cs
	xor	ax, ax
	mov	es, ax
	mov	ax, cs
	mov	ds, ax
	mov	si, LABEL_GDT
	mov	di, PHY_ADDR_GDT
	mov	ecx, 4 * 2
	rep	movsd	; ds:LABEL_GDT -> 0:2K
	xor	eax, eax
	mov	ecx, (2048 - 4 * 8) / 4
	rep	stosd

	; Set Page directory.
	; es:0
	xor	eax, eax
	mov	ecx, 1024
	mov	di, PHY_ADDR_PAGE_DIR
	rep	stosd	; Zero all first
	mov	dword [es:PHY_ADDR_PAGE_DIR], PHY_ADDR_PAGE_TABLE_LOW | 7
	mov	dword [es:PHY_ADDR_PAGE_DIR + 0C00h], PHY_ADDR_PAGE_TABLE_HIGH | 7

	; Set Page table.
	;  1. page-table-low
	; es:0
	mov	ebx, 1000h
	mov	ecx, 1024
	mov	eax, 0 + 7
	mov	di, PHY_ADDR_PAGE_TABLE_LOW
.1:	stosd
	add	eax, ebx
	loop	.1
	;  2. page-table-high
	; es:0	ds:0
	xor	ax, ax
	mov	ds, ax
	mov	si, PHY_ADDR_PAGE_TABLE_LOW
	mov	di, PHY_ADDR_PAGE_TABLE_HIGH
	mov	ecx, 1024
	rep	movsd

	; Go to the protected mode.
	mov	ax, cs
	mov	ds, ax
	lgdt	[gdt_ptr]
	cli
	in	al, 92h
	or	al, 10b
	out	92h, al
	mov	eax, cr0
	or	eax, 1
	or	eax, 00010000h	; WP, for do_exec
	mov	cr0, eax

	; Enable the page!
	mov	eax, PHY_ADDR_PAGE_DIR
	mov	cr3, eax
	mov	eax, cr0
	or	eax, 80000000h
	mov	cr0, eax

	jmp	dword sel_code:(PHY_ADDR_LOADER + LABEL_PROTECT)


[BITS 32]
LABEL_PROTECT:
	mov	ax, sel_data
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	esp, TopStack
	mov	ax, sel_video
	mov	gs, ax

	; Load the GDT in virtual address.
	lgdt	[gdt_ptr1 - HEAD_LABEL + PHY_ADDR_LOADER]

	; Initialize the kernel.
	mov	eax, PHY_ADDR_KERNEL_FILE
	movzx	ebx, word [eax + 2Ah]	; size of head
	movzx	edx, word [eax + 2Ch]	; number of heads
	add	ax, word [eax + 1Ch]	; offset of heads
.1:
	mov	esi, [eax]
	test	esi, esi
	jz	.noAction
	mov	ecx, dword [eax + 10h]	; file size
	mov	esi, PHY_ADDR_KERNEL_FILE
	add	esi, dword [eax + 4h]	; source
	mov	edi, dword [eax + 8h]	; desteny
	sub	edi, 0C0000000h
	rep	movsb
	; Zero the mem_sz - file_sz.
	mov	ecx, [eax + 14h]	; memory size
	cmp	ecx, [eax + 10h]	; file size
	je	.noAction
	sub	ecx, [eax + 10h]
	mov	[9FBFCh], eax
	xor	al, al
	rep	stosb
	mov	eax, [9FBFCh]

.noAction:
	add	eax, ebx
	dec	edx
	jnz	.1

	; Jump to the kernel!!
	jmp	dword [PHY_ADDR_KERNEL_FILE + 18h]
